package cn.faifai.tagapi.service;

import cn.faifai.common.exception.file.FileNotExistException;
import cn.faifai.common.page.TableDataInfo;
import cn.faifai.framework.service.FileService;
import cn.faifai.framework.utils.FileUtils;
import cn.faifai.framework.utils.HashUtils;
import cn.faifai.framework.utils.SQLUtils;
import cn.faifai.framework.utils.SingleEvenBus;
import cn.faifai.framework.utils.fetcher.fileIdFetcher.FileIdFetcher;
import cn.faifai.framework.utils.fetcher.fileIdFetcher.FileIdFetcherFactory;
import cn.faifai.tagapi.config.HardLinksConfig;
import cn.faifai.tagapi.entity.IFile;
import cn.faifai.tagapi.entity.Tag;
import cn.faifai.tagapi.dto.*;
import cn.faifai.tagapi.enums.fileSaveCode;
import cn.faifai.tagapi.events.FileUrlTraversalException;
import cn.faifai.tagapi.repository.FileUrlRepository;
import cn.faifai.tagapi.repository.TagRepository;
import cn.hutool.core.bean.BeanUtil;
import jakarta.persistence.criteria.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
public class FileUrlService {

    @Autowired
    private FileService fileService;

    @Autowired
    private FileUrlRepository fileUrlRepository;

    @Autowired
    private TagRepository tagRepository;

    public FileSerialize getFileUrlInfo(Long id){
        return new FileSerialize(fileUrlRepository.findById(id).orElse(null));
    }

    public List<String> getSubPath(String path){
        // 将字符串路径转换为Path对象
        Path dirPath = Paths.get(path);

        // 检查路径是否存在并且是一个目录
        if (!Files.exists(dirPath) || !Files.isDirectory(dirPath)) {
            log.error("指定的路径不存在或不是一个目录: " + path);
            return Arrays.asList(path);
        }

        try (Stream<Path> stream = Files.list(dirPath)) {
            // 将路径流转换为字符串列表
            return stream.map(subPath -> subPath.toString())
                    .collect(Collectors.toList());
        } catch (IOException e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }


    public SubDirectoryDto scanDirectory(String path) {
        SubDirectoryDto subDirectoryInfo = new SubDirectoryDto(new ArrayList<>(), new ArrayList<>());
        if (path.endsWith(":")){
            path = path + "//";
        }
        Path dirPath = Paths.get(path);
        // 检查路径是否存在并且是一个目录
        if (!Files.exists(dirPath)) {
            log.error("指定路径不存在: " + path);
            return subDirectoryInfo;
        }
        if (!Files.isDirectory(dirPath)){
            log.error("指定路径不是一个目录: " + path);
            subDirectoryInfo.getFiles().add(path);
            return subDirectoryInfo;
        }
        try (Stream<Path> stream = Files.list(dirPath)) {
            stream.forEach(subPath -> {
                if (Files.isDirectory(subPath)) {
                    subDirectoryInfo.getDirectories().add(subPath.toString());
                } else {
                    subDirectoryInfo.getFiles().add(subPath.toString());
                }
            });
        } catch (IOException e) {
            log.error("文件遍历失败: " + path, e);
            throw new RuntimeException(e);
        }

        return subDirectoryInfo;
    }

    /**
     * 可将文件注册到数据库中，如果是目录，则报错
     */
    public Response registerFile(String systemPath) throws IOException, NoSuchAlgorithmException {
        Response result = new Response();
        Path path = Paths.get(systemPath);
        if (Files.isDirectory(path)){
            throw new RuntimeException("不能注册目录");
        }
        IFile iFile = this.initIFile(systemPath);
        IFile save = fileUrlRepository.save(iFile);
        result.setCode(save == null ? fileSaveCode.FILE_NOT_REGISTER.getCode() : fileSaveCode.FILE_REGISTERED.getCode());
        result.setData(save);
        String hardLink = iFile.getDisk()+ HardLinksConfig.DIRECTORY+"\\"+save.getId()+"."+iFile.getSuffix();
        FileUtils.createHardLink(path.toString(), hardLink);
        return result;
    }

    /**
     * 打开文件
     */
    public Void openFile(String systemPath) throws IOException {
        Path path = Paths.get(systemPath);
        if (Files.isDirectory(path)){
            throw new RuntimeException("不能打开目录");
        }
        if (!Files.exists(path)){
            throw new RuntimeException("文件不存在");
        }
        try {
//            Desktop.getDesktop().open(path.toFile()); // 使用默认的应用程序打开文件
            String pathStr = path.toString();
            String[] cmd = new String[]{"cmd.exe", "/c", "start", "\"\"", pathStr};
            ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", "start", "\"\"", pathStr);
            processBuilder.start();
        } catch (IOException e) {
            System.err.println("无法打开文件: " + e.getMessage());
        }
        return null;
    }

    /**
     * 桌面打开文件
     */
    public void openFileDesktop(String systemPath) {
        Path path = Paths.get(systemPath);
        if (Files.isDirectory(path)){
            throw new RuntimeException("不能打开目录");
        }
        if (!Files.exists(path)){
            throw new RuntimeException("文件不存在");
        }
        try {
            String fileId = FileIdFetcherFactory.getFileIdFetcher().getFileId(path.toString());
            IFile file = fileUrlRepository.findByOsFileId(fileId);
            if (file == null){
                throw new FileNotExistException(null,null,null,"文件不存在");
            }
            Path openPath = Paths.get(path.getRoot() + "\\" + HardLinksConfig.DIRECTORY + "\\" + file.getId() + "." + file.getSuffix());
            Desktop.getDesktop().open(openPath.toFile());
        } catch (FileNotExistException e) {
            Path openFile = Paths.get(systemPath);
            try {
                Desktop.getDesktop().open(openFile.toFile());
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        } catch (IOException e) {
            System.err.println("无法打开文件: " + e.getMessage());
        }
    }

    /**
     * 初始化IFile对象
     */
    public IFile initIFile(String systemPath) throws IOException, NoSuchAlgorithmException {
        Path path = Paths.get(systemPath);
        if (Files.isDirectory(path)){
            throw new RuntimeException("不能初始化目录");
        }
        IFile file = new IFile();
        file.setDisk(fileService.getFileSystemMountPoint(systemPath));
        try (FileInputStream fis = new FileInputStream(path.toFile())){
            file.setSHA256Str(HashUtils.calculateHash(fis, "SHA-256"));
        }
        file.setSuffix(FileUtils.getFileSuffix(systemPath));
        file.setFileName(path.getFileName().toString());
        file.setPath(path.toString());
        file.setHardLinkStatus(1);
        String os = System.getProperty("os.name");
        FileIdFetcher fileIdFetcher = FileIdFetcherFactory.getFileIdFetcher(os);
        file.setOsFileId(fileIdFetcher.getFileId(path.toString()));
        file.setOs(os);
        return file;
    }

    public Integer tagFile(FileUrlDto fileUrlDto) throws IOException, NoSuchAlgorithmException {
        if (fileUrlDto.getId() == null){
            return fileSaveCode.FILE_NOT_REGISTER.getCode();
        }
        Optional<IFile> file = fileUrlRepository.findById(fileUrlDto.getId());
        List<Tag> newTags = tagRepository.findAllById(fileUrlDto.getTagIds());
        if (file.isPresent()){
            HashSet<Tag> currentTags = new HashSet<>(file.map(IFile::getTags).orElse(Collections.emptySet()));
            currentTags.addAll(newTags);
            file.get().setTags(currentTags);
        }else {
            return fileSaveCode.FILE_NOT_REGISTER.getCode();
        }
        IFile save = fileUrlRepository.save(file.get());
        return save != null ? fileSaveCode.SUCCESS.getCode() : fileSaveCode.FILE_NOT_REGISTER.getCode();
    }

    /**
     * 置换文件标签
     */
    public Integer replaceTag(FileUrlDto fileUrlDto) throws IOException, NoSuchAlgorithmException {
        if (fileUrlDto.getId() == null){
            return fileSaveCode.FILE_NOT_REGISTER.getCode();
        }
        Optional<IFile> file = fileUrlRepository.findById(fileUrlDto.getId());
        List<Tag> newTags = tagRepository.findAllById(fileUrlDto.getTagIds());
        if (file.isPresent()){
            file.get().setTags(new HashSet<>(newTags));
        } else {
            return fileSaveCode.FILE_NOT_REGISTER.getCode();
        }
        IFile save = fileUrlRepository.save(file.get());
        return save != null ? fileSaveCode.SUCCESS.getCode() : fileSaveCode.FILE_NOT_REGISTER.getCode();
    }

    /**
     * 检查文件是否注册
     */
    public Response checkFileRegister(String systemPath) throws IOException, NoSuchAlgorithmException {
        Response response = new Response();
        response.setCode(fileSaveCode.FILE_NOT_EXIST.getCode());
        Path path = Paths.get(systemPath);
        if (Files.isDirectory(path)){
            throw new RuntimeException("不能初始化目录");
        }
        if (!Files.exists(path)){
            return response;
        }
        FileIdFetcher fileIdFetcher = FileIdFetcherFactory.getFileIdFetcher(System.getProperty("os.name"));
        String osFileId = fileIdFetcher.getFileId(path.toString());
        IFile byOsFileId = fileUrlRepository.findByOsFileId(osFileId);
        if (byOsFileId != null){
            response.setCode(fileSaveCode.FILE_REGISTERED.getCode());
            response.setData(byOsFileId);
            return response;
        }else {
            return response;
        }
    }

    /**
     * 根据路径获取文件注册信息，如果没有注册则返回未初始化对象
     */
    public IFile getFileRegisterInfo(String systemPath) {
        Path path = Paths.get(systemPath);
        if (!Files.exists(path)){
            return new IFile();
        }
        FileIdFetcher fileIdFetcher = FileIdFetcherFactory.getFileIdFetcher(System.getProperty("os.name"));
        String osFileId = null;
        try {
            osFileId = fileIdFetcher.getFileId(path.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        IFile byOsFileId = fileUrlRepository.findByOsFileId(osFileId);
        return byOsFileId;
    }

    /**
     * 获取文件标签信息
     */
    public List<TagDTO> getFileTags(String systemPath) {
        Path path = Paths.get(systemPath);
        if (!Files.exists(path)){
            return new ArrayList<>();
        }
        FileIdFetcher fileIdFetcher = FileIdFetcherFactory.getFileIdFetcher(System.getProperty("os.name"));
        String osFileId = null;
        try {
            osFileId = fileIdFetcher.getFileId(path.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        List<Tag> tags = tagRepository.findByIFilesOsFileId(osFileId);
        List<TagDTO> tagDTOS = BeanUtil.copyToList(tags, TagDTO.class);
        return tagDTOS;
    }

    /**
     * 获取注册文件列表
     */
    public TableDataInfo getFileList(FileUrlQuery<IFile> query, PageQuery pageQuery) {
        Page<IFile> filePage = fileUrlRepository.findAll(
                buildSpecification(query),
                PageRequest.of(pageQuery.getJPAPageNum(), pageQuery.getPageSize()));
        return TableDataInfo.build(filePage);
    }

    /**
     * 构造动态条件
     */
    public Specification<IFile> buildSpecification(FileUrlQuery<IFile> fileQuery) {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (fileQuery.getId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("id"), fileQuery.getId()));
            }
            if (StringUtils.isNotBlank(fileQuery.getPath())) {
                predicates.add(criteriaBuilder.like(root.get("path"), SQLUtils.getLike(fileQuery.getPath())));
            }
            if (StringUtils.isNotBlank(fileQuery.getSHA256Str())) {
                predicates.add(criteriaBuilder.equal(  root.get("SHA256Str"), fileQuery.getSHA256Str()));
            }
            if (StringUtils.isNotBlank(fileQuery.getSuffix())){
                predicates.add(criteriaBuilder.equal(root.get("suffix"), fileQuery.getSuffix()));
            }
            if (fileQuery.getPreDeleteStatus() != null){
                predicates.add(criteriaBuilder.equal(root.get("preDeleteStatus"), fileQuery.getPreDeleteStatus()));
            }
            if (StringUtils.isNotBlank(fileQuery.getDisk())){
                predicates.add(criteriaBuilder.like(root.get("disk"), SQLUtils.getLike(fileQuery.getDisk())));
            }
            if (StringUtils.isNotBlank(fileQuery.getFileName())){
                predicates.add(criteriaBuilder.like(root.get("fileName"), SQLUtils.getLike(fileQuery.getFileName())));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
    }

    /**
     * 测试
     * @param args
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
        Path path = Paths.get("D://code//note//补码1.png");
        // 确保提供了一个正确的文件路径
        File imageFile = path.toFile();

        if (imageFile.exists()) { // 检查文件是否存在
            try {
                Desktop.getDesktop().open(imageFile); // 使用默认的应用程序打开文件
            } catch (IOException e) {
                System.err.println("无法打开文件: " + e.getMessage());
            }
        } else {
            System.out.println("文件不存在");
        }
    }

    public TableDataInfo getFileListByTagId(Long tagId, PageQuery pageQuery) {
        Page<IFile> filePage = fileUrlRepository.findAllByTagsId(tagId,
                PageRequest.of(pageQuery.getJPAPageNum(), pageQuery.getPageSize()));
        return TableDataInfo.build(filePage);
    }

    /**
     * 删除注册文件--仅删除注册文件
     * @param ids
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteRegisterFile(List<Long> ids) {
        List<IFile> allById = fileUrlRepository.findAllById(ids);
        fileUrlRepository.deleteAllById(ids);
        allById.forEach(file -> {
            try {
                String hardLink = file.getDisk() + "/HardLinks/" + file.getId() + "." + file.getSuffix();
                Files.delete(Paths.get(hardLink));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    public Boolean hasSubPath(String path) {
        final Path dir = Paths.get(path);
        if (!Files.exists(dir) || !Files.isDirectory(dir)) {
            return false;
        }

        final AtomicBoolean hasSubdirs = new AtomicBoolean(false);
        // 使用 Files.newDirectoryStream 来遍历目录
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, Files::isDirectory)) {
            for (Path entry : stream) {
                // 如果找到至少一个子文件夹，则返回 true
                return true;
            }
        } catch (IOException e) {
            SingleEvenBus.getInstance().post(new FileUrlTraversalException());
        }


        return hasSubdirs.get();
    }
}
